import numpy as np
from sklearn.cluster import KMeans
from collections import defaultdict

class MicroCluster:
    def __init__(self, timestamp, data_point):
        self.N = 1  # 数据点数
        self.LS = data_point  # 线性求和
        self.SS = data_point**2  # 平方和
        self.timestamp = timestamp  # 最后更新时间戳

    def add_point(self, data_point, timestamp):
        self.N += 1
        self.LS += data_point
        self.SS += data_point**2
        self.timestamp = timestamp

    def centroid(self):
        return self.LS / self.N

class CluStream:
    def __init__(self, horizon=1000, micro_clusters=100):
        self.horizon = horizon  # 时间窗口长度
        self.micro_clusters = micro_clusters  # 最大微簇数
        self.clusters = []  # 当前微簇列表
        self.time = 0

    def online_update(self, data_point):
        # 寻找最近微簇
        nearest = None
        min_dist = float('inf')
        for cluster in self.clusters:
            dist = np.linalg.norm(data_point - cluster.centroid())
            if dist < min_dist:
                min_dist = dist
                nearest = cluster

        # 合并或新建微簇
        if nearest and min_dist < self.radius_threshold():
            nearest.add_point(data_point, self.time)
        else:
            new_cluster = MicroCluster(self.time, data_point)
            self.clusters.append(new_cluster)

        # 删除过期微簇
        self.clusters = [c for c in self.clusters 
                        if (self.time - c.timestamp) <= self.horizon]

        self.time += 1

    def offline_clustering(self, k=5):
        # 提取微簇中心点进行宏聚类
        centroids = [cluster.centroid() for cluster in self.clusters]
        return KMeans(n_clusters=k).fit_predict(centroids)

    def radius_threshold(self):
        # 动态调整合并半径（示例简化逻辑）
        return 2.0 * (1 + np.log(len(self.clusters) + 1))


if __name__ == "__main__":
    # 生成流数据（示例用随机数据）
    stream_data = np.random.randn(1000, 2)  # 1000个二维数据点

    # 初始化CluStream
    clustream = CluStream(horizon=500, micro_clusters=50)

    # 在线更新微簇
    for point in stream_data:
        clustream.online_update(point)

    # 离线获取最终聚类结果
    macro_labels = clustream.offline_clustering(k=3)